package com.haixiaoke.saas.repository.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.reflect.TypeToken;
import com.haixiaoke.saas.repository.dto.*;
import com.haixiaoke.saas.repository.exception.BizException;
import com.haixiaoke.saas.repository.mapper.OrderMapper;
import com.haixiaoke.saas.repository.orderStateMachine.OrderStateEnum;
import com.haixiaoke.saas.repository.pojo.*;
import com.haixiaoke.saas.repository.service.*;
import com.haixiaoke.saas.repository.util.GsonUtil;
import com.haixiaoke.saas.repository.vo.HouseConditionVO;
import com.haixiaoke.saas.repository.vo.OrderAmountVO;
import com.haixiaoke.saas.repository.vo.OrderInfoVO;
import com.haixiaoke.saas.repository.vo.OrderVO;
import com.mzj.saas.commons.StatusCode;
import com.mzj.saas.commons.util.DataUtils;
import com.mzj.saas.commons.util.StringUtils;
import com.mzj.saas.sdk.pay.vo.WxPay;
import com.mzj.saas.sdk.pay.wxpay.WxPayRequest;
import com.mzj.saas.sdk.refund.vo.WxRefund;
import com.mzj.saas.sdk.refund.wx.WxRefundRequest;
import jodd.typeconverter.Convert;
import jodd.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 订单Service业务层处理
 *
 * @author haixiaoke
 * @date 2023-04-12
 */
@Slf4j
@Service
public class OrderServiceImpl implements IOrderService {
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private IHouseService iHouseService;        //房屋service
    @Autowired
    private IHousingAllocationService iHousingAllocationService;    //房屋配置service
    @Autowired
    private IOrderOperatingRecordService iOrderOperatingRecordService;//订单异常记录
    @Autowired
    private IAreaService iAreaService;          //地域service
    @Autowired
    private ICommunityService iCommunityService;
    @Autowired
    private IOrderAmountItemService iOrderAmountItemService;
    @Autowired
    private IOrderLockService iOrderLockService;
    @Autowired
    private IOrderWechatPayRecordService iOrderWechatPayRecordService;
    @Autowired
    private IHouseFacilitiesItemService iHouseFacilitiesItemService;
    @Autowired
    private ICommunityBuildService iCommunityBuildService;
    @Autowired
    private IAppUserService iAppUserService;
    @Autowired
    private ICommunityHouseUserService iCommunityHouseUserService;
    @Autowired
    private ICalendarPriceService iCalendarPriceService;
    @Autowired
    private IPayTypeService iPayTypeService;
    @Autowired
    private ISmartLockService iSmartLockService;
    @Autowired
    private IApartmentBillService iApartmentBillService;
    @Autowired
    private IHouseInspectionFacilityService iHouseInspectionFacilityService;
    @Autowired
    private IApartmentOrderChargeItemService iApartmentOrderChargeItemService;
    @Autowired
    private IApartmentItemRuleRefService iApartmentItemRuleRefService;
    @Autowired
    private IApartmentChargeItemUsageService iApartmentChargeItemUsageService;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private IUserService iUserService;
    @Autowired
    private IHouseInspectionService iHouseInspectionService;
    @Value("${apartment.pay}")
    private boolean apartmentPay;

    /**
     * 查询订单
     *
     * @param orderId 订单主键
     * @return 订单
     */
    @Override
    public OrderVO selectOrderDetailsByOrderId(String orderId) {
        Order order = orderMapper.selectOrderByOrderId(orderId);

        if (ObjectUtil.isEmpty(order)) return null;

        //封装订单数据
        this.packagingOrder(order);
        if (HousingAllocation.APARTMENT_TYPE_LONG.equals(order.getApartmentType())) {
            HousingAllocation housingAllocation = GsonUtil.toBean(order.getAllocationContent(), HousingAllocation.class);
            PayType payType = iPayTypeService.selectPayTypeById(housingAllocation.getPayTypeId());
            order.setApartmentPayType(payType);
        }

        List<OrderAmountItem> items = iOrderAmountItemService.selectOrderAmountItemByOrderId(orderId);
        if (HousingAllocation.APARTMENT_TYPE_SHORT.equals(order.getApartmentType())) {
            for (OrderAmountItem x : items) {
                if ((OrderAmountItem.GOODS_TYPE_HOUSE.equals(x.getGoodsType()) || OrderAmountItem.GOODS_TYPE_RE_LET.equals(x.getGoodsType())) && StrUtil.isNotEmpty(x.getAmountDetail())) {
                    List<AmountDetailDTO> detailDTOList = GsonUtil.toBeanCollectors(x.getAmountDetail(), new TypeToken<List<AmountDetailDTO>>() {
                    }.getType());
                    x.setAmountDetailDTOList(detailDTOList);
                }
            }
        }
        OrderVO orderVO = new OrderVO();
        BeanUtil.copyProperties(order, orderVO);
        if (Order.ORDER_STATE_CANCELED.equals(order.getOrderState())) {
            OrderOperatingRecord record = iOrderOperatingRecordService.selectOperatingOrderCancel(order.getOrderId(), order.getOrderState(), Order.ORDER_STATE_UN_PAYMENT);
            if (ObjectUtil.isNotEmpty(record)) orderVO.setOperatingType(record.getOperatingType());
        }
        orderVO.setAmountItems(items);

        OrderOperatingRecord orderOperatingRecord = iOrderOperatingRecordService.selectOrderOperatingRecordByOrderId(orderId);
        if (ObjectUtil.isNotEmpty(orderOperatingRecord)) {
            orderVO.setUserType(orderOperatingRecord.getUserType());
        }
        return orderVO;
    }

    /**
     * 预订人id查询订单
     *
     * @param reservationId 预订人id
     * @return 订单
     */
    @Override
    public List<Order> selectOrderByReservationId(String reservationId) {
        return orderMapper.selectOrderByReservationId(reservationId);
    }

    /**
     * 查询订单列表
     *
     * @param order 订单
     * @return 订单
     */
    @Override
    public List<Order> selectOrderList(Order order) {
        List<Order> orders = orderMapper.selectOrderList(order);
        if (CollectionUtil.isNotEmpty(orders)) {
            List<String> houseIds = orders.stream()
                    .map(Order::getHouseId)
                    .distinct()
                    .collect(Collectors.toList());
            Map<String, Map<String, Object>> brandIdMap = iSmartLockService.selectBrandIdByHouseId(houseIds);
            for (Order apartmentOrder : orders) {
                //封装订单数据
                Map<String, Object> smartLockMap = brandIdMap.get(apartmentOrder.getHouseId());
                if (MapUtil.isNotEmpty(smartLockMap)) {
                    Long brandId = (Long) smartLockMap.get("brand_id");
                    apartmentOrder.setBrandId(brandId.intValue());
                }
                this.packagingOrder(apartmentOrder);
            }
        }
        return orders;
    }

    /**
     * 新增订单
     *
     * @param order 订单
     * @return 结果
     */
    @Override
    public int insertOrder(Order order) {
        order.setCreateTime(new Date());
        return orderMapper.insertOrder(order);
    }

    /**
     * 修改订单
     *
     * @param order 订单
     * @return 结果
     */
    @Override
    public int updateOrder(Order order) {
        order.setUpdateTime(new Date());
        return orderMapper.updateOrder(order);
    }

    /**
     * 批量删除订单
     *
     * @param orderIds 需要删除的订单主键
     * @return 结果
     */
    @Override
    public int deleteOrderByOrderIds(Long[] orderIds) {
        return orderMapper.deleteOrderByOrderIds(orderIds);
    }

    /**
     * 删除订单信息
     *
     * @param orderId 订单主键
     * @return 结果
     */
    @Override
    public int deleteOrderByOrderId(Long orderId) {
        return orderMapper.deleteOrderByOrderId(orderId);
    }

    /**
     * 查询订单所选的房屋及房屋配置信息
     *
     * @param houseId 房屋id
     * @return
     */
    @Override
    public HouseAndAllocationDTO selectHouseAndAllocation(String houseId) {
        log.info("房屋的houseId为：{}", houseId);
        House house = iHouseService.selectHouseById(houseId); //查询房屋数据
        //封装房屋数据
        Order order = new Order();
        BeanUtil.copyProperties(house, order);
        if (house.getResidenceCommunityId() != null && house.getResidenceCommunityName() == null) {
            Community community = iCommunityService.selectCommunityById(house.getResidenceCommunityId());
            order.setResidenceCommunityName(community.getName());
        }
        this.packagingData(order);
        HousingAllocation housingAllocation = iHousingAllocationService.selectHousingAllocationByHouseId(houseId);//查询房屋配置数据
        order.setApartmentPicture(housingAllocation.getApartmentPicture().split(",")[0]);
        if (ObjectUtil.isEmpty(house) || !House.HOUSE_TYPE_APARTMENT.equals(house.getHouseNature()) || ObjectUtil.isEmpty(housingAllocation))
            return null;
        List<HouseFacilitiesItem> houseFacilitiesItems = iHouseFacilitiesItemService.selectHouseFacilitiesItemByHouseId(houseId);
        HouseAndAllocationDTO dto = new HouseAndAllocationDTO();
        if (HousingAllocation.APARTMENT_TYPE_SHORT.equals(housingAllocation.getApartmentType())) {
            List<CalendarPrice> calendarPriceList = iCalendarPriceService.selectCalendarPriceByHouseId(houseId);
            dto.setCalendarPriceList(calendarPriceList);
        } else if (HousingAllocation.APARTMENT_TYPE_LONG.equals(housingAllocation.getApartmentType())) {
            PayType payType = iPayTypeService.selectPayTypeById(housingAllocation.getPayTypeId());
            dto.setPayType(payType);
        }
        dto.setHouse(house);
        dto.setHousingAllocation(housingAllocation);
        dto.setHouseFacilitiesItemList(houseFacilitiesItems);
        dto.setOrder(order);
        String cacheKey = "S" + System.currentTimeMillis() + RandomUtils.nextInt(1000, 10000);
        //订单号
        dto.setCacheKey(cacheKey);
        log.info("房屋的基本信息为:{}", GsonUtil.toJson(dto));
        return dto;
    }

    /**
     * 根据房屋配置的金额，计算金额，并统计金额
     *
     * @param calendarPriceList 日历时间段集合
     * @param payType           押付方式
     * @param housingAllocation 房屋配置
     * @param orderDTO          订单入住时间、退房时间
     * @param cashPledgeBoolean 是否计算租金
     * @return 订单总金额
     */
    @Override
    public OrderAmountVO computedAmount(List<CalendarPrice> calendarPriceList, PayType payType, HousingAllocation housingAllocation, OrderDTO orderDTO, Boolean cashPledgeBoolean) {
        OrderAmountVO vo = new OrderAmountVO();
        List<OrderAmountItem> items = new ArrayList<>();
        Date checkInTime = orderDTO.getCheckInTime();
        Date checkOutTime = orderDTO.getCheckOutTime();
        if (checkInTime == null || checkOutTime == null) {
            return null;
        }
        //入住时间在退房时间之后，则退出方法
        if (checkInTime.after(checkOutTime)) return null;
        long day = 0;
        List<CalendarPriceDTO> splitList = null;
        List<AmountDetailDTO> amountDetailDTOList = new ArrayList<>();
        //短租公寓计算方式
        if (HousingAllocation.APARTMENT_TYPE_SHORT.equals(housingAllocation.getApartmentType())) {
            if (CollectionUtil.isNotEmpty(calendarPriceList)) {
                splitList = iCalendarPriceService.calendarPriceSplit(calendarPriceList, checkInTime, checkOutTime);
            }
            if (CollectionUtil.isEmpty(splitList)) {
                day = DateUtil.between(checkInTime, checkOutTime, DateUnit.DAY);
            } else {
                for (int i = 0; i < splitList.size() - 1; i++) {
                    if (splitList.get(i).getEndTime().before(splitList.get(i + 1).getStartTime())) {
                        long betweenDay = DateUtil.between(splitList.get(i).getEndTime(), splitList.get(i + 1).getStartTime(), DateUnit.DAY);
                        AmountDetailDTO amountDetailDTO = new AmountDetailDTO(splitList.get(i).getEndTime(), splitList.get(i + 1).getStartTime(), (double) betweenDay, null, null, null, OrderAmountItem.AMOUNT_TYPE_COMMON);
                        amountDetailDTOList.add(amountDetailDTO);
                        day += betweenDay;
                    }
                }
                if (checkInTime.before(splitList.get(0).getStartTime())) {
                    long betweenDay = DateUtil.between(checkInTime, splitList.get(0).getStartTime(), DateUnit.DAY);
                    AmountDetailDTO amountDetailDTO = new AmountDetailDTO(checkInTime, splitList.get(0).getStartTime(), (double) betweenDay, null, null, null, OrderAmountItem.AMOUNT_TYPE_COMMON);
                    amountDetailDTOList.add(amountDetailDTO);
                    day += betweenDay;
                }
                if (checkOutTime.after(splitList.get(splitList.size() - 1).getEndTime())) {
                    long betweenDay = DateUtil.between(splitList.get(splitList.size() - 1).getEndTime(), checkOutTime, DateUnit.DAY);
                    AmountDetailDTO amountDetailDTO = new AmountDetailDTO(splitList.get(splitList.size() - 1).getEndTime(), checkOutTime, (double) betweenDay, null, null, null, OrderAmountItem.AMOUNT_TYPE_COMMON);
                    amountDetailDTOList.add(amountDetailDTO);
                    day += betweenDay;
                }
            }
            //长租公寓计算方式
        } else if (HousingAllocation.APARTMENT_TYPE_LONG.equals(housingAllocation.getApartmentType())) {
            int startDay = DateUtil.dayOfMonth(checkInTime);
            int endDay = DateUtil.dayOfMonth(checkOutTime);
            if (startDay - endDay > 0) {
                int startMonth = DateUtil.month(checkInTime) + 1;
                int endMonth = DateUtil.month(checkOutTime) + 1;
                // 条件判断 1,3,5,7,8,10,12月为31天
                if (startMonth == 1 || startMonth == 3 || startMonth == 5 || startMonth == 7 || startMonth == 8 || startMonth == 10 || startMonth == 12) {
                    // 条件判断 start月为31天
                    if (endMonth == 4 || endMonth == 6 || endMonth == 9 || endMonth == 11) {
                        //结束日期的月份为30天
                        if (endDay != 30) {
                            throw new BizException(Integer.valueOf(StatusCode.ERROR_CODE_10005.getErrorCode()), StatusCode.ERROR_CODE_10005.getErrorMsg());
                        }
                    } else if (endMonth == 2) {
                        //根据年来判断2月份的天数
                        int endYear = DateUtil.year(checkOutTime);
                        if ((endYear % 100 != 0 && endYear % 4 == 0) || endYear % 400 == 0) {
                            if (endDay != 29) {
                                throw new BizException(Integer.valueOf(StatusCode.ERROR_CODE_10005.getErrorCode()), StatusCode.ERROR_CODE_10005.getErrorMsg());
                            }
                        } else {
                            if (endDay != 28) {
                                throw new BizException(Integer.valueOf(StatusCode.ERROR_CODE_10005.getErrorCode()), StatusCode.ERROR_CODE_10005.getErrorMsg());
                            }
                        }
                    }
                }
            }
            day = DateUtil.betweenMonth(checkInTime, checkOutTime, false);
        }
        //用户入住的时间
        if (day <= 0 && CollectionUtil.isEmpty(splitList)) return null;
        //计算价格总额
        BigDecimal amount = null;
        Double goodsAmount = null;
        if (HousingAllocation.APARTMENT_TYPE_SHORT.equals(housingAllocation.getApartmentType())) {
            if (HousingAllocation.CHARGE_TYPE_IMMOBILIZATION.equals(housingAllocation.getChargeType())) {   //房屋配置是固定收费的
                goodsAmount = housingAllocation.getFixedPrice();
                amount = new BigDecimal(day).multiply(BigDecimal.valueOf(goodsAmount));
            } else if (HousingAllocation.CHARGE_TYPE_UN_IMMOBILIZATION.equals(housingAllocation.getChargeType())) {//房屋配置是时效收费的
                Map<Integer, Double> map = GsonUtil.toMapInteger(housingAllocation.getAgingPrice());
                List<Integer> keyList = new ArrayList<>(map.keySet());//key的list集合
                Collections.sort(keyList);
                if (keyList.get(keyList.size() - 1) <= day) {    //入住时间是否大于最大时效入住天数
                    goodsAmount = map.get(keyList.get(keyList.size() - 1));
                    amount = new BigDecimal(day)
                            .multiply(BigDecimal.valueOf(goodsAmount));    //入住天数*map对应的value时效费用（key为list最后的元素）
                } else if (keyList.get(0) >= day) {         //入住时间小于等于最小时效入住天数
                    goodsAmount = map.get(keyList.get(0));
                    amount = new BigDecimal(day)
                            .multiply(BigDecimal.valueOf(goodsAmount));    //入住天数*map对应的value时效费用（key为list第一位的元素）
                } else {
                    for (int i = keyList.size() - 1; i > 0; i--) {
                        if (keyList.get(i - 1) <= day && day < keyList.get(i)) {  //入住时间是否大于等于当前索引的value，并小于下一个索引value
                            goodsAmount = map.get(keyList.get(i - 1));
                            amount = new BigDecimal(day).multiply(BigDecimal.valueOf(goodsAmount));//入住天数*map对应的value时效费用（key为入住天数>=的元素，且key<下一个索引）
                        }
                    }
                }
            }
            if (CollectionUtil.isNotEmpty(amountDetailDTOList)) {
                double v = amount.doubleValue();
                amountDetailDTOList.forEach(x -> {
                    if (x.getItemSum() == null) {
                        x.setItemSum(v);
                    }
                });
            }
            if (CollectionUtil.isNotEmpty(splitList)) {
                for (CalendarPriceDTO dto : splitList) {
                    long splitDay = DateUtil.between(dto.getStartTime(), dto.getEndTime(), DateUnit.DAY);
                    if (splitDay > 0) {
                        //日历天数*特价费用 + 原有的日常日期费用
                        BigDecimal splitAmount = new BigDecimal(splitDay).multiply(BigDecimal.valueOf(dto.getPrice()));
                        amount = amount.add(splitAmount);
                        AmountDetailDTO amountDetailDTO = new AmountDetailDTO(dto.getStartTime(), dto.getEndTime(), Double.valueOf(splitDay), dto.getPrice(), splitAmount.doubleValue(), null, OrderAmountItem.AMOUNT_TYPE_SPECIAL);
                        amountDetailDTOList.add(amountDetailDTO);
                    }
                }
            }
        } else if (HousingAllocation.APARTMENT_TYPE_LONG.equals(housingAllocation.getApartmentType())) {
            goodsAmount = housingAllocation.getMonthPrice();
            //月份数*月价费
            amount = new BigDecimal(payType.getPayMonth()).multiply(BigDecimal.valueOf(housingAllocation.getMonthPrice()));
            AmountDetailDTO payDTO = new AmountDetailDTO(checkInTime, checkOutTime, payType.getPayMonth().doubleValue(), housingAllocation.getMonthPrice(), amount.doubleValue(), amount.doubleValue(), OrderAmountItem.AMOUNT_TYPE_SPECIAL);
            amountDetailDTOList.add(payDTO);
        }
        String amountContent = null;
        if (CollectionUtil.isNotEmpty(amountDetailDTOList)) {
            double a = amount.doubleValue();
            double g = goodsAmount;
            amountDetailDTOList.forEach(x -> {
                if (x.getTotalPrice() == null) {
                    x.setTotalPrice(a);
                }
                if (x.getGoodsAmount() == null) {
                    x.setGoodsAmount(g);
                }
            });
            amountContent = GsonUtil.toJson(amountDetailDTOList);
        }
        long sort = 0;
        int itemType = 0;
        if (cashPledgeBoolean) {
            sort = 10L;
            itemType = OrderAmountItem.GOODS_TYPE_HOUSE;
        } else {
            sort = 40L;
            itemType = OrderAmountItem.GOODS_TYPE_RE_LET;
        }
        OrderAmountItem item = new OrderAmountItem(null, orderDTO.getCacheKey(), null, goodsAmount,
                (double) day, amount == null ? 0 : amount.doubleValue(), itemType, null, amountContent, sort);

        items.add(item);

        // region 租金计算
        if (cashPledgeBoolean) {
            double cashPledgeNum = 1;
            Double cashPledge = null;
            double cashPledgeCount = 0;
            //计算押金和保洁费的价格--短租才计算押金
            if (HousingAllocation.APARTMENT_TYPE_SHORT.equals(housingAllocation.getApartmentType())) {
                cashPledge = housingAllocation.getCashPledge();
                cashPledgeCount = housingAllocation.getCashPledge();
                if (amount == null) {
                    amount = BigDecimal.valueOf(cashPledge);
                } else {
                    amount = amount.add(BigDecimal.valueOf(cashPledge));
                }
            } else if (HousingAllocation.APARTMENT_TYPE_LONG.equals(housingAllocation.getApartmentType())) {
                cashPledgeNum = payType.getDepositMonth();
                cashPledge = housingAllocation.getMonthPrice();
                BigDecimal deposit = new BigDecimal(payType.getDepositMonth()).multiply(BigDecimal.valueOf(housingAllocation.getMonthPrice()));
                cashPledgeCount = deposit.doubleValue();
                amount = amount.add(deposit);
            }

            OrderAmountItem cashPledgeItem = new OrderAmountItem(null, orderDTO.getCacheKey(), null, cashPledge,
                    cashPledgeNum, cashPledgeCount, OrderAmountItem.GOODS_TYPE_CASHPLEDGE, null, null, 20L);
            items.add(cashPledgeItem);
        }
        // endregion

        if (Order.CLEANING_TYPE_YES.equals(orderDTO.getCleaningType())) {
            if (amount == null) {
                amount = BigDecimal.valueOf(housingAllocation.getCleaningPrice());
            } else {
                amount = amount.add(BigDecimal.valueOf(housingAllocation.getCleaningPrice()));
            }
            OrderAmountItem cleaningItem = new OrderAmountItem(null, orderDTO.getCacheKey(), null, housingAllocation.getCleaningPrice(),
                    (double) 1, housingAllocation.getCleaningPrice(), OrderAmountItem.GOODS_TYPE_CLEANING, null, null, 30L);
            items.add(cleaningItem);
        }
        vo.setAmount(amount);
        vo.setOrderAmountItemList(items);
        if (HousingAllocation.APARTMENT_TYPE_LONG.equals(housingAllocation.getApartmentType())) vo.setPayType(payType);
        log.info("订单id：{}，计算金额为：{}，房屋配置收费类型为：{}，固定收费为：{}，时效收费为：{}，begin：{}、end：{}、day：{}", orderDTO.getCacheKey(), amount,
                housingAllocation.getChargeType(), housingAllocation.getFixedPrice(), housingAllocation.getAgingPrice(), checkInTime, checkOutTime, day);
        return vo;
    }

    /**
     * 校验订单的状态--false为订单校验不通过
     *
     * @param cacheKey 订单id
     * @return
     */
    @Override
    public Boolean checkOrderState(String cacheKey) {
        Order order = orderMapper.selectOrderByOrderId(cacheKey);
        if (ObjectUtil.isNotEmpty(order)) {
            //订单状态不为”未支付“，则订单校验不同过
            if (!Objects.equals(order.getOrderState(), Order.ORDER_STATE_UN_PAYMENT)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 创建订单 true为创建成功
     *
     * @param orderDTO
     */
    @Override
    public Order createOrder(OrderDTO orderDTO, HouseAndAllocationDTO dto) {
        int result = 0;
        String cardNumber = iAppUserService.selectAppUserById(dto.getTokenRedisVo().getId());
        //校验用户是否实名认证了
        if (StringUtil.isEmpty(cardNumber))
            throw new BizException(Integer.valueOf(StatusCode.ERROR_CODE_50001.getErrorCode()), StatusCode.ERROR_CODE_50001.getErrorMsg());

        Order order = new Order();
        DateTime createTime = DateUtil.date();
        OrderAmountVO vo = null;
        if (ObjectUtil.isNotEmpty(dto)) {
            BeanUtil.copyProperties(dto.getHouse(), order);
            order.setHouseContent(GsonUtil.toJson(dto.getHouse()));                 //房屋的快照数据
            order.setAllocationContent(GsonUtil.toJson(dto.getHousingAllocation()));//房屋配置的快照数据
            if (ObjectUtil.isNotEmpty(dto.getHouseFacilitiesItemList())) {
                order.setFacilitiesContent(GsonUtil.toJson(dto.getHouseFacilitiesItemList()));
            }
            String reservationPhone = StringUtils.isEmpty(orderDTO.getReservationPhone()) ? dto.getTokenRedisVo().getPhone() : orderDTO.getReservationPhone();        //预定人电话
            order.setReservationPhone(reservationPhone);
            order.setReservationId(dto.getTokenRedisVo().getId());              //预定人id
            order.setReservationName(dto.getTokenRedisVo().getName());          //预定人名称
            order.setReservationCardNumber(cardNumber);                         //预定人身份证号码
            order.setHouseId(dto.getHouse().getId());                           //房屋id
            order.setAllocationId(dto.getHousingAllocation().getAllocationId());//房屋配置id
            order.setCheckInTime(orderDTO.getCheckInTime());                    //入住时间
            order.setCheckOutTime(orderDTO.getCheckOutTime());                  //退房时间
            order.setCreateBy(dto.getTokenRedisVo().getId());                   //创建人id
            order.setCreateTime(createTime);                               //订单创建时间
            order.setOrderState(Order.ORDER_STATE_UN_PAYMENT);
            order.setCleaningType(orderDTO.getCleaningType());
            order.setOrderSource(Order.ORDER_SOURCE_MINI_PROGRAM);
            if (dto.getHouse().getOwnerType() == 1) {
                order.setHouseOwnerId(String.valueOf(dto.getHouse().getOrgId()));
                //查询产权单位下的主管账户电话
                String ownerPhone = iAppUserService.selectPhoneByOrgId(dto.getHouse().getOrgId());
                order.setHouseOwnerPhone(ownerPhone);
            } else if (dto.getHouse().getOwnerType() == 2) {
                order.setHouseOwnerId(dto.getHouse().getOwnerUserId());
                order.setHouseOwnerPhone(dto.getHouse().getOwnerMobile());
            }
            order.setHouseOwnerName(dto.getHouse().getOwnerName());
            //计算金额
            vo = this.computedAmount(dto.getCalendarPriceList(), dto.getPayType(), dto.getHousingAllocation(), orderDTO, true);//订单价格总额
            double amountDouble = vo.getAmount().doubleValue();
            vo.getOrderAmountItemList().forEach(x -> {
                x.setTotalPrice(amountDouble);
                x.setCreateBy(dto.getTokenRedisVo().getId());
                x.setCreateTime(createTime);
                x.setOrderCheckInTime(orderDTO.getCheckInTime());
                x.setOrderCheckOutTime(orderDTO.getCheckOutTime());
            });
            order.setPriceAmounts(vo.getAmount().doubleValue());
            order.setOrderId(orderDTO.getCacheKey());                           //订单编号
            //如果小区名称为空，则去查询并封装小区名称
            if (dto.getHouse().getResidenceCommunityId() != null && StringUtils.isEmpty(dto.getHouse().getResidenceCommunityName())) {
                Community community = iCommunityService.selectCommunityById(dto.getHouse().getResidenceCommunityId());
                order.setResidenceCommunityName(community.getName());
            }
            ArrayList<Long> list = new ArrayList<>();
            if (order.getProvinceId() != null) list.add(order.getProvinceId());
            if (order.getCityId() != null) list.add(order.getCityId());
            if (order.getDistrictId() != null) list.add(order.getDistrictId());
            if (order.getStreetId() != null) list.add(order.getStreetId());
            if (order.getCommunityId() != null) list.add(order.getCommunityId());
            Map<Long, String> collect = null;
            if (!list.isEmpty()) {
                Long[] areaIds = list.toArray(new Long[0]);
                List<Area> areas = iAreaService.selectAreaList(areaIds);
                collect = areas.stream().collect(Collectors.toMap(Area::getId, Area::getName));
            }
            if (collect != null && !collect.isEmpty()) {
                if (order.getProvinceId() != null) order.setProvinceName(collect.get(order.getProvinceId()));
                if (order.getCityId() != null) order.setCityName(collect.get(order.getCityId()));
                if (order.getDistrictId() != null) order.setDistrictName(collect.get(order.getDistrictId()));
                if (order.getStreetId() != null) order.setStreetName(collect.get(order.getStreetId()));
                if (order.getCommunityId() != null) order.setCommunityName(collect.get(order.getCommunityId()));
            }
            //封装数据
            this.packagingData(order);
            result = orderMapper.insertOrder(order);
            log.info("创建的订单数据：{}", GsonUtil.toJson(order));
        }
        if (result != 0) {
            //设置订单过期时间
            redisTemplate.opsForValue().set(Order.REDIS_ORDER_KEY_LOCK + orderDTO.getCacheKey(), order, 30, TimeUnit.MINUTES);
            OrderOperatingRecord record = new OrderOperatingRecord();
            record.setOrderId(order.getOrderId());      //订单id
            record.setOrderState(Order.ORDER_STATE_UN_PAYMENT); //订单状态
            record.setOperatingBy(order.getReservationId());    //操作人id
            record.setOperatingTime(createTime);                //操作时间
            record.setRemark(OrderOperatingRecord.RECORD_REMARK_UN_PAYMENT);//备注内容
            //添加订单操作记录
            iOrderOperatingRecordService.insertOrderOperatingRecord(record);
            //添加订单金额计算的详细信息
            /*Object o = redisTemplate.opsForValue().get(OrderAmountItem.REDIS_ORDER_AMOUNT_ITEM + orderDTO.getCacheKey());

            if (ObjectUtil.isNull(o)) {
                throw new BizException(Convert.toInteger(StatusCode.TIMEOUT_UN_PAY_40002.getErrorCode()), "订单已过期, 请重新下单");
            }*/
            //订单金额计算项目
//            List<OrderAmountItem> items = GsonUtil.toListExt(Convert.toString(o), OrderAmountItem.class);
            if (ObjectUtil.isNotEmpty(vo))
                iOrderAmountItemService.insertBatchOrderAmountItem(vo.getOrderAmountItemList());
            //锁单数据
            OrderLock lock = new OrderLock(null, order.getOrderId(), order.getHouseId(), order.getReservationId(), order.getCheckInTime(), order.getCheckOutTime());
            iOrderLockService.insertOrderLock(lock);
        }
        return result != 0 ? order : null;
    }

    /**
     * 修改订单的状态
     *
     * @param order
     * @return
     */
    @Override
    public int updateOrderState(Order order) {
        return orderMapper.updateOrderState(order);
    }

    @Override
    public Map<String, Object> orderWxPay(OrderDTO orderDTO, HouseAndAllocationDTO houseAndAllocationDTO, Order order) {
        // 单位： 分
        Integer amountCount = Convert.toInteger(NumberUtil.mul(order.getPriceAmounts().doubleValue(), 100));

        WxPay wxpay = new WxPay(orderDTO.getAppId(), orderDTO.getMchId(), orderDTO.getCacheKey(),
                String.valueOf(houseAndAllocationDTO.getHouse().getOrgId()), apartmentPay? amountCount : 1,    //todo 金钱单位以分
                "房屋管家-公寓支付", orderDTO.getMchApiKey(), orderDTO.getOpenId(),
                orderDTO.getWxNotifyUrl(), orderDTO.getUnifiedApi(),
                orderDTO.getSpbillCreateIp());
        log.info("小程序支付的类--WxPay:{}", GsonUtil.toJson(wxpay));
        Map<String, Object> result = new WxPayRequest(wxpay).unifiedByMini();
        if (CollectionUtil.isEmpty(result)) {
            throw new BizException(Integer.valueOf(StatusCode.ERROR_CODE_10011.getErrorCode()), StatusCode.ERROR_CODE_10011.getErrorMsg());
        }
        result.put("orderId", orderDTO.getCacheKey());
        return result;
    }

    @Override
    public Map<String, Object> reletOrderWxPay(OrderDTO orderDTO, Order order) {
        // 单位： 分
        Integer amountCount = Convert.toInteger(NumberUtil.mul(order.getPriceAmounts().doubleValue(), 100));

        WxPay wxpay = new WxPay(orderDTO.getAppId(), orderDTO.getMchId(), orderDTO.getCacheKey(),
                String.valueOf(order.getOrgId()), apartmentPay? amountCount : 1,    //todo 金钱单位以分
                "房屋管家-公寓续租支付", orderDTO.getMchApiKey(), orderDTO.getOpenId(),
                orderDTO.getWxNotifyUrl(), orderDTO.getUnifiedApi(),
                orderDTO.getSpbillCreateIp());
        log.info("小程序支付的类--WxPay:{}", GsonUtil.toJson(wxpay));
        Map<String, Object> result = new WxPayRequest(wxpay).unifiedByMini();
        if (CollectionUtil.isEmpty(result)) {
            throw new BizException(Integer.valueOf(StatusCode.ERROR_CODE_10011.getErrorCode()), StatusCode.ERROR_CODE_10011.getErrorMsg());
        }
        result.put("orderId", orderDTO.getCacheKey());
        return result;
    }

    @Override
    public HouseAndAllocationDTO parseHouseMsg(String redisKey) {
        if (StringUtils.isEmpty(redisKey)) return null;
        Object o = redisTemplate.opsForValue().get(redisKey);
        return GsonUtil.toBean(GsonUtil.toJson(o), HouseAndAllocationDTO.class);
    }

    /**
     * 根据订单id查订单基本信息
     *
     * @param orderId 订单id
     * @return Order
     */
    @Override
    public Order selectOrderById(String orderId) {
        return orderMapper.selectOrderByOrderId(orderId);
    }

    @Override
    public List<Order> selectOrderListValid(Order order) {
        String[] houseIdList = iCommunityHouseUserService.selectCommunityHouseUserByMobile(order.getReservationPhone());
        List<Order> orders = orderMapper.selectOrderListValid(order);
        if (houseIdList != null && houseIdList.length > 0) {
            List<Order> shareWithOrders = orderMapper.selectOrderListByHouseIds(houseIdList);
            orders.addAll(shareWithOrders);
        }
        orders.stream().sorted(Comparator.comparing(Order::getCreateTime).reversed())
                .collect(Collectors.toList());
//        orders = orders.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Order::getOrderId))), ArrayList::new));
        Map<Long, HousingAllocation> allocationMap = new HashMap<>();
        if (CollectionUtil.isNotEmpty(orders)) {

            //查询房屋配置信息
            List<Long> allocationId = orders.stream().map(Order::getAllocationId).collect(Collectors.toList());
            List<HousingAllocation> allocations = iHousingAllocationService.selectByAllocationIds(allocationId);
            allocationMap = allocations.stream().collect(Collectors.toMap(HousingAllocation::getAllocationId, HousingAllocation -> HousingAllocation));
        }
        for (Order orgOrder : orders) {
            String fullRoom = orgOrder.getResidenceCommunityName() + orgOrder.getBuildingNumName() + "栋" + orgOrder.getUnitName() + "单元" + orgOrder.getRoomNumber() + "号";
            orgOrder.setFullRoom(fullRoom);

            HousingAllocation housingAllocation = allocationMap.get(orgOrder.getAllocationId());
            if (ObjectUtil.isNotEmpty(housingAllocation)) {
                orgOrder.setStewardPhone(housingAllocation.getStewardPhone());
            }

            if (order.getReservationId().equals(orgOrder.getReservationId())) {
                orgOrder.setOrderType(Order.ORDER_TYPE_OWNER);
            } else {
                orgOrder.setOrderType(Order.ORDER_TYPE_SHARE_WITH);
            }
        }
        return orders;
    }

    @Override
    public Map<String, Object> orderRePay(Order order, OrderDTO orderDTO) {
        Integer amountCount = Convert.toInteger(NumberUtil.mul(order.getPriceAmounts().doubleValue(), 100));
        WxPay wxpay = new WxPay(orderDTO.getAppId(), orderDTO.getMchId(), orderDTO.getCacheKey(),
                String.valueOf(order.getOrgId()), apartmentPay? amountCount : 1,    //todo 金钱单位以分
                "房屋管家-公寓支付", orderDTO.getMchApiKey(), orderDTO.getOpenId(),
                orderDTO.getWxNotifyUrl(), orderDTO.getUnifiedApi(),
                orderDTO.getSpbillCreateIp());
        Map<String, Object> result = new WxPayRequest(wxpay).unifiedByMini();
        if (CollectionUtil.isEmpty(result)) {
            throw new BizException(Integer.valueOf(StatusCode.ERROR_CODE_10011.getErrorCode()), StatusCode.ERROR_CODE_10011.getErrorMsg());
        }
        result.put("orderId", orderDTO.getCacheKey());
        return result;
    }

    @Override
    public List<Map<String, Object>> getStateList() {
        List<Map<String, Object>> orderList = new ArrayList<>();
        try {
            Class<Enum> clz = (Class<Enum>) Class.forName(OrderStateEnum.class.getName());
            Object[] objects = clz.getEnumConstants();
            Method getCode = clz.getMethod("getValue");
            Method getMessage = clz.getMethod("getLabel");
            for (Object object : objects) {
                Map<String, Object> stateMap = new HashMap<>();
                stateMap.put("value", getCode.invoke(object));
                stateMap.put("label", getMessage.invoke(object));
                orderList.add(stateMap);
            }
            return orderList;
        } catch (Exception e) {
            log.error("获取订单常量失败：{}", e.getMessage());
        }
        return orderList;
    }

    @Override
    public Map<String, String> orderRefund(OrderWxRefundDTO orderWxRefundDTO) {
        OrderWechatPayRecord record = iOrderWechatPayRecordService.selectByOrderId(orderWxRefundDTO.getOrderId());
        String transactionId = ObjectUtil.isEmpty(record) ? null : record.getTransactionId();
        Integer amountCount = Convert.toInteger(NumberUtil.mul(orderWxRefundDTO.getOrderAmounts().doubleValue(), 100));
        Integer refundAmount = Convert.toInteger(NumberUtil.mul(orderWxRefundDTO.getRefundAmounts().doubleValue(), 100));
        WxRefund wxRefund = new WxRefund(orderWxRefundDTO.getAppId(), orderWxRefundDTO.getMchId(), orderWxRefundDTO.getMchApiKey(), transactionId, orderWxRefundDTO.getOrderId(),
                orderWxRefundDTO.getOrderId(), apartmentPay ? amountCount : 1, apartmentPay ? refundAmount : 1, orderWxRefundDTO.getWxNotifyUrl(), orderWxRefundDTO.getMchCertificate(), orderWxRefundDTO.getUnifiedApi(), null);
        WxRefundRequest wxRefundRequest = new WxRefundRequest(wxRefund);
        Map<String, String> result = wxRefundRequest.refund();
        if (CollectionUtil.isEmpty(result)) {
            throw new BizException(Integer.valueOf(StatusCode.ERROR_CODE_10012.getErrorCode()), StatusCode.ERROR_CODE_10012.getErrorMsg());
        }
        return result;
    }

    /**
     * @param checkInTime       入住时间
     * @param checkOutTime      退房时间
     * @param rentableStartTime 可租开始时间
     * @param rentableEndTime   可租结束时间
     * @return true 通过     false  不通过
     */
    @Override
    public Boolean checkTimeValid(Date checkInTime, Date checkOutTime, Date rentableStartTime, Date rentableEndTime) {
        //入住时间在可租开始时间之后  &&  退房时间在可租结束时间之前  &&  入住时间在退房时间之前  &&  可租开始时间在可租结束时间之前
        return (checkInTime.getTime() - rentableStartTime.getTime()) >= 0 && ((rentableEndTime.getTime() - checkOutTime.getTime()) >= 0) && checkInTime.before(checkOutTime) && rentableStartTime.before(rentableEndTime);
    }

    @Override
    public Order selectOrderStateById(String orderId) {
        return orderMapper.selectOrderStateById(orderId);
    }

    @Override
    public OrderAmountVO reletOrder(OrderDTO orderDTO, Order order, HousingAllocation housingAllocation) {

        DateTime nowTime = DateUtil.date();
        HousingAllocation oldHousingAllocation = GsonUtil.toBean(order.getAllocationContent(), HousingAllocation.class);
        housingAllocation.setApartmentType(oldHousingAllocation.getApartmentType());
        //订单如果还没购买保洁费，那么根据续租订单的数据封装数据
        if (Order.CLEANING_TYPE_NO.equals(order.getCleaningType())) {
            housingAllocation.setCleaningPrice(oldHousingAllocation.getCleaningPrice());
        }
        List<CalendarPrice> calendarPriceList = null;
        PayType payType = null;
        if (HousingAllocation.APARTMENT_TYPE_SHORT.equals(housingAllocation.getApartmentType())) {
            calendarPriceList = iCalendarPriceService.selectCalendarPriceByHouseId(order.getHouseId());
        } else if (HousingAllocation.APARTMENT_TYPE_LONG.equals(housingAllocation.getApartmentType())) {
            payType = iPayTypeService.selectPayTypeById(housingAllocation.getPayTypeId());
        }
        OrderAmountVO vo = this.computedAmount(calendarPriceList, payType, housingAllocation, orderDTO, false);
        if (vo == null) return null;
        double amountDouble = vo.getAmount().doubleValue();
        vo.getOrderAmountItemList().forEach(x -> {
            x.setCreateTime(nowTime);
            x.setCreateBy(orderDTO.getUpdateBy());
            x.setTotalPrice(amountDouble);
            x.setOrderCheckInTime(order.getCheckOutTime());
            x.setOrderCheckOutTime(orderDTO.getCheckOutTime());
        });
        //原有订单价格增加续租价格费用
        order.setPriceAmounts(vo.getAmount().add(BigDecimal.valueOf(order.getPriceAmounts())).doubleValue());
        order.setCheckOutTime(orderDTO.getCheckOutTime());
        order.setUpdateTime(nowTime);
        order.setUpdateBy(orderDTO.getUpdateBy());
        if (!orderDTO.getCleaningType().equals(order.getCleaningType()))
            order.setCleaningType(orderDTO.getCleaningType());
        OrderReletDTO reletDTO = new OrderReletDTO();

        OrderOperatingRecord record = new OrderOperatingRecord();
        record.setOrderId(orderDTO.getCacheKey());      //订单id
        record.setOrderSourceState(order.getOrderState());
        record.setOrderState(order.getOrderState()); //订单状态
        record.setOperatingBy(orderDTO.getUpdateBy());    //操作人id
        record.setOperatingTime(nowTime);                //操作时间
        record.setRemark(OrderOperatingRecord.RECORD_REMARK_RE_LET);//备注内容
        OrderLock lock = new OrderLock();
        lock.setOrderId(order.getOrderId());
        lock.setCheckOutTime(orderDTO.getCheckOutTime());
        reletDTO.setOrder(order);
        reletDTO.setOrderLock(lock);
        reletDTO.setOrderOperatingRecord(record);
        reletDTO.setOrderAmountItemList(vo.getOrderAmountItemList());
        redisTemplate.opsForValue().set(Order.REDIS_ORDER_KEY_RELET + orderDTO.getCacheKey(), JSONObject.toJSON(reletDTO), 30, TimeUnit.MINUTES);
        //封装续租统计的订单总金额
        order.setPriceAmounts(vo.getAmount().doubleValue());
        return vo;
    }

    @Override
    public List<Order> selectOrderByOrderState(Integer orderState) {
        return orderMapper.selectOrderByOrderState(orderState);
    }

    @Override
    public int updateReLetOrder(Order order) {
        return orderMapper.updateReLetOrder(order);
    }

    @Override
    public void deleteUnRelet(String orderId) {
        String[] split = orderId.split("_");
        Order order = this.selectOrderById(split[0]);
        iOrderAmountItemService.deleteOrderAmountItemByOrderId(split[0]);
        iOrderOperatingRecordService.deleteOrderOperatingRecordByOrderId(orderId);
        OrderLock orderLock = new OrderLock();
        orderLock.setCheckOutTime(order.getCheckOutTime());
        iOrderLockService.updateOrderLock(orderLock);
        order.setReletNum(order.getReletNum() - 1);
        this.updateReLetOrder(order);
    }

    @Override
    public OrderCheckOutDTO checkOutInfo(String orderId) {
        BigDecimal bigDecimal = BigDecimal.ZERO;
        OrderCheckOutDTO dto = new OrderCheckOutDTO();
        Order order = orderMapper.selectOrderByOrderId(orderId);

        if (ObjectUtil.isEmpty(order)) {
            return null;
        }

        //查询订单收费项、账单、验房设施信息
        List<OrderAmountItem> items = iOrderAmountItemService.selectOrderAmountItemByOrderId(orderId);
        List<ApartmentBill> apartmentBills = iApartmentBillService.selectApartmentBillByOrderId(orderId);
        List<HouseInspectionFacility> houseInspectionFacilities = iHouseInspectionFacilityService.selectHouseInspectionFacilityByOrderId(orderId);

        //押金
        OrderAmountItem cashPledgeItem = items.stream()
                .filter(x -> OrderAmountItem.GOODS_TYPE_CASHPLEDGE.equals(x.getGoodsType()) && OrderAmountItem.GOODS_REFUND_NO.equals(x.getRefundType()))
                .findFirst()
                .orElse(null);
        //房租
        OrderAmountItem houseItem = items.stream()
                .filter(x -> OrderAmountItem.GOODS_TYPE_HOUSE.equals(x.getGoodsType()) && OrderAmountItem.GOODS_REFUND_NO.equals(x.getRefundType()))
                .findFirst()
                .orElse(null);
        //续租
        List<OrderAmountItem> reletItems = items.stream().filter(item -> OrderAmountItem.GOODS_TYPE_RE_LET.equals(item.getGoodsType()) && OrderAmountItem.GOODS_REFUND_NO.equals(item.getRefundType())).collect(Collectors.toList());
        HousingAllocation housingAllocation = GsonUtil.toBean(order.getAllocationContent(), HousingAllocation.class);
        //统计余额
        bigDecimal = computedBalance(order, cashPledgeItem, houseItem, reletItems, bigDecimal, housingAllocation);

        //统计损坏设施金额
        BigDecimal facilitiesDecimal = BigDecimal.ZERO;
        List<HouseInspectionFacility> result = null;
        if (CollectionUtil.isNotEmpty(houseInspectionFacilities)) {
            List<HouseInspectionFacility> userInspectionFacilities = houseInspectionFacilities.stream().filter(x -> HouseInspectionFacility.ORDER_STATE_USER_HOUSE_INSPECTION.equals(x.getInspectionStatus())).collect(Collectors.toList());
            List<HouseInspectionFacility> enterpriseInspectionFacilities = houseInspectionFacilities.stream().filter(x -> HouseInspectionFacility.ORDER_STATE_ENTERPRISE_LEASE_HOUSE_INSPECTION.equals(x.getInspectionStatus())).collect(Collectors.toList());
            List<Long> facilitiesIds = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(userInspectionFacilities)) {
                for (HouseInspectionFacility enterprise : enterpriseInspectionFacilities) {
                    //设施损坏后，和租户验房时比较是否由租客损坏的
                    if (HouseInspectionFacility.FACILITIES_STATE_YES.equals(enterprise.getFacilitiesState())) {
                        for (HouseInspectionFacility user : userInspectionFacilities) {
                            if (enterprise.getFacilitiesId().equals(user.getFacilitiesId())) {
                                //如果商家验房是损坏状态，而租户入住时是正常，则扣费
                                if (!enterprise.getFacilitiesState().equals(user.getFacilitiesState())) {
                                    //
                                    facilitiesIds.add(enterprise.getFacilitiesId());
                                }
                            }
                        }
                    }
                }
            } else {
                facilitiesIds = enterpriseInspectionFacilities.stream().filter(x -> HouseInspectionFacility.FACILITIES_STATE_YES.equals(x.getFacilitiesState()))
                        .map(HouseInspectionFacility::getFacilitiesId).collect(Collectors.toList());
            }
            List<Long> finalFacilitiesIds = facilitiesIds;
            if (CollectionUtil.isNotEmpty(finalFacilitiesIds)) {
                result = enterpriseInspectionFacilities.stream()
                        .filter(obj -> finalFacilitiesIds.contains(obj.getFacilitiesId()))
                        .collect(Collectors.toList());
                for (HouseInspectionFacility facility : result) {
                    facilitiesDecimal = BigDecimal.valueOf(facility.getFacilitiesPrice()).add(facilitiesDecimal);
                }
            }
            int compareTo = bigDecimal.compareTo(facilitiesDecimal);
            if (compareTo >= 0) {
                bigDecimal = bigDecimal.subtract(facilitiesDecimal);
            } else {
                //账单生成
                bigDecimal = BigDecimal.ZERO;
                redisTemplate.opsForValue().set(HouseInspectionFacility.HOUSEINSPECTIONFACILITY_REDIS_KEY + orderId, result, 60, TimeUnit.MINUTES);
            }
        }


        HousingAllocation newHousingAllocation = iHousingAllocationService.selectHousingAllocationById(order.getAllocationId());
        dto.setOrder(order);
        dto.setRefundNum(bigDecimal.doubleValue());
        dto.setApartmentBills(apartmentBills);
        dto.setHouseInspectionFacilities(result);
        if (ObjectUtil.isNotEmpty(cashPledgeItem)) {
            dto.setCashPledge(cashPledgeItem.getItemSum());
        } else {
            dto.setCashPledge(0d);
        }
        if (ObjectUtil.isNotEmpty(newHousingAllocation)) {
            dto.setCleaningPrice(newHousingAllocation.getCleaningPrice());
        } else {
            dto.setCleaningPrice(0d);
        }
        double v = bigDecimal.subtract(BigDecimal.valueOf(cashPledgeItem.getItemSum())).doubleValue();
        dto.setOtherNum(v <=0 ? 0 : v);
        return dto;
    }

    @Override
    public void orderRebates(Order order, OrderWxRefundDTO refund, OrderCheckOutDTO orderCheckOutDTO) {
        HousingAllocation allocation = GsonUtil.toBean(order.getAllocationContent(), HousingAllocation.class);
        order.setTotalRefundAmounts(orderCheckOutDTO.getRefundNum());
        BigDecimal refundNum = BigDecimal.valueOf(orderCheckOutDTO.getRefundNum());
        BigDecimal actualRefundNum = BigDecimal.valueOf(orderCheckOutDTO.getActualRefundNum());
        BigDecimal orderRefundNum = BigDecimal.ZERO;
        List<OrderAmountItem> items = iOrderAmountItemService.selectOrderAmountItemByOrderId(order.getOrderId());
        Map<String, List<OrderAmountItem>> itemMap = items.stream()
                .collect(Collectors.groupingBy(OrderAmountItem::getOrderId));
        for (String orderId : itemMap.keySet()) {
            List<OrderAmountItem> list = itemMap.get(orderId);
            BigDecimal total = BigDecimal.valueOf(list.get(0).getTotalPrice());
            refund.setOrderId(list.get(0).getOrderId());
            refund.setOrderAmounts(list.get(0).getTotalPrice());
            if (actualRefundNum.compareTo(total) > 0) {
                refund.setRefundAmounts(total.doubleValue());
                Map<String, String> result = this.orderRefund(refund);
                if (CollectionUtil.isNotEmpty(result) && "SUCCESS".equals(result.get("result_code"))) {
                    orderRefundNum = orderRefundNum.add(total);
                    actualRefundNum = actualRefundNum.subtract(total);
                } else {
                    log.error("部分退款失败响应参数：{}", result);
                }
            } else if (actualRefundNum.compareTo(total) <= 0) {
                refund.setRefundAmounts(actualRefundNum.doubleValue());
                Map<String, String> result = this.orderRefund(refund);
                if (CollectionUtil.isNotEmpty(result) && "SUCCESS".equals(result.get("result_code"))) {
                    orderRefundNum = orderRefundNum.add(actualRefundNum);
                    break;
                } else {
                    log.error("部分退款失败响应参数：{}", result);
                }
            }
        }

        order.setRefundAmounts(orderRefundNum.doubleValue());

        //账单数据
        Date nowTime = DateUtil.date();
        Object o = redisTemplate.opsForValue().get(HouseInspectionFacility.HOUSEINSPECTIONFACILITY_REDIS_KEY + orderCheckOutDTO.getOrderId());
        List<HouseInspectionFacility> facilities = GsonUtil.toBeanCollectors(GsonUtil.toJson(o), new TypeToken<List<HouseInspectionFacility>>() {
        }.getType());
        if (CollectionUtil.isNotEmpty(facilities)) {

            //修改金额
            int compareTo = refundNum.compareTo(actualRefundNum);
            if (compareTo > 0) {        //退款金额比实际退款金额大
                //减
                BigDecimal priceDifference = refundNum.subtract(actualRefundNum);
                for (HouseInspectionFacility facility : facilities) {
                    if (priceDifference.compareTo(BigDecimal.ZERO) > 0) {
                        BigDecimal facilityPrice = BigDecimal.valueOf(facility.getFacilitiesPrice());
                        int facilityCompare = priceDifference.compareTo(facilityPrice);
                        if (facilityCompare >= 0) {
                            priceDifference = priceDifference.subtract(facilityPrice);
                            facility.setFacilitiesPrice(0d);
                        } else {
                            BigDecimal subtract = facilityPrice.subtract(priceDifference);
                            facility.setFacilitiesPrice(subtract.doubleValue());
                            priceDifference = BigDecimal.ZERO;
                        }
                    }
                }
                facilities = facilities.stream().filter(x -> x.getFacilitiesPrice() != 0).collect(Collectors.toList());
            } else if (compareTo < 0) {   //退款金额比实际退款金额小
                //加
                BigDecimal priceDifference = actualRefundNum.subtract(refundNum);
                BigDecimal newDecimal = BigDecimal.valueOf(facilities.get(0).getFacilitiesPrice()).add(priceDifference);
                facilities.get(0).setFacilitiesPrice(newDecimal.doubleValue());
            }

            List<ApartmentOrderChargeItem> apartmentItems = new ArrayList<>();
            for (HouseInspectionFacility facility : facilities) {
                ApartmentOrderChargeItem apartmentItem = new ApartmentOrderChargeItem();
                apartmentItem.setOrderId(orderCheckOutDTO.getOrderId());
                apartmentItem.setItemUnit("件");
                apartmentItem.setItemName(facility.getFacilitiesName());
                apartmentItem.setCreateBy(orderCheckOutDTO.getUserId());
                apartmentItem.setCreateTime(nowTime);
                apartmentItems.add(apartmentItem);
            }
            int itemBatch = iApartmentOrderChargeItemService.insertApartmentOrderChargeItemBatch(apartmentItems);

            //“biz_apartment_item_rule_ref”插入数据
            ApartmentBill apartmentBill = iApartmentBillService.selectApartmentBillByOrderIdOne(orderCheckOutDTO.getOrderId());
            if (itemBatch > 0) {
//                if (HousingAllocation.APARTMENT_TYPE_LONG.equals(allocation.getApartmentType())) {
                Map<String, List<HouseInspectionFacility>> facilityMap = facilities.stream()
                        .collect(Collectors.groupingBy(HouseInspectionFacility::getFacilitiesName));
                List<ApartmentItemRuleRef> refs = new ArrayList<>();
                for (ApartmentOrderChargeItem apartmentItem : apartmentItems) {
                    List<HouseInspectionFacility> facilityList = facilityMap.get(apartmentItem.getItemName());
                    if (CollectionUtil.isNotEmpty(facilityList)) {
                        if (apartmentItem.getItemId() != null) {
                            ApartmentItemRuleRef ref = new ApartmentItemRuleRef();
                            ref.setRuleId(apartmentBill.getRuleId());
                            ref.setItemId(apartmentItem.getItemId());
                            ref.setOrderId(orderCheckOutDTO.getOrderId());
                            ref.setItemType(ApartmentItemRuleRef.ITEM_TYPE_ORDER_ITEM);
                            ref.setItemName(apartmentItem.getItemName());
                            ref.setItemUnit("件");
                            ref.setUnitPrice(facilityList.get(0).getFacilitiesPrice());
                            facilityList.remove(0);
                            facilityMap.put(apartmentItem.getItemName(), facilityList);
                            ref.setCreateBy(orderCheckOutDTO.getUserId());
                            ref.setCreateTime(nowTime);
                            refs.add(ref);
                        }
                    }
                }
                int refBatch = iApartmentItemRuleRefService.insertApartmentItemRuleRefBatch(refs);


                //"biz_apartment_charge_item_usage"插入数据
                if (refBatch > 0) {
                    List<ApartmentChargeItemUsage> usages = new ArrayList<>();
                    for (ApartmentItemRuleRef ref : refs) {
                        if (ref.getRefId() != null) {
                            ApartmentChargeItemUsage usage = new ApartmentChargeItemUsage();
                            usage.setBillId(apartmentBill.getBillId());
                            usage.setRefId(ref.getRefId());
                            usage.setItemName(ref.getItemName());
                            usage.setUnitPrice(ref.getUnitPrice());
                            usage.setNumber(1.0);
                            usage.setTotalPrice(ref.getUnitPrice());
                            usage.setCreateBy(orderCheckOutDTO.getUserId());
                            usage.setCreateTime(nowTime);
                            usages.add(usage);
                        }
                    }
                    iApartmentChargeItemUsageService.insertApartmentChargeItemUsageBatch(usages);
                }
/*
                } else if (HousingAllocation.APARTMENT_TYPE_SHORT.equals(allocation.getApartmentType())) {
                    //"biz_apartment_bill"插入新数据


                    //"biz_apartment_charge_item_usage"插入数据
//                    List<ApartmentChargeItemUsage> usages = new ArrayList<>();
//                    for (ApartmentOrderChargeItem apartmentItem : apartmentItems) {
                         //TODO: 2023/12/18 封装数据
//                    }
//                    iApartmentChargeItemUsageService.insertApartmentChargeItemUsageBatch(usages);
                }
*/


            }
        }
    }

    @Override
    public Order selectOrderByHouseId(String houseId) {
        return orderMapper.selectOrderByHouseId(houseId);
    }

    @Override
    public Order rebatesInfo(String orderId) {
        Order order = orderMapper.selectOrderByOrderId(orderId);
        if (Order.CLEANING_TYPE_NO.equals(order.getCleaningType())) {
            order.setCleaningPrice(0d);
        }else if (Order.CLEANING_TYPE_YES.equals(order.getCleaningType())){
            OrderAmountItem orderAmountItem = iOrderAmountItemService.selectOrderAmountItemByOrderIdAndGoodsType(orderId,OrderAmountItem.GOODS_TYPE_CLEANING);
            order.setCleaningPrice(orderAmountItem.getItemSum());
        }
        OrderAmountItem orderAmountItem = iOrderAmountItemService.selectOrderAmountItemByOrderIdAndGoodsType(orderId, OrderAmountItem.GOODS_TYPE_CASHPLEDGE);
        order.setCashPledge(orderAmountItem.getItemSum());
        return order;
    }

    @Override
    public List<Order> selectOrderByIds(String[] orderIds) {
        return orderMapper.selectOrderByIds(orderIds);
    }

    @Override
    public OrderInfoVO orderInfo(String orderId) {
        OrderInfoVO orderInfoVO = new OrderInfoVO();
        Order order = orderMapper.selectOrderByOrderId(orderId);
        List<CommunityHouseUser> communityHouseUserList = iCommunityHouseUserService.selectCommunityHouseUserByOrderId(orderId);
//        List<HouseInspectionFacility> houseInspectionFacilityList = iHouseInspectionFacilityService.selectHouseInspectionFacilityByOrderId(orderId);
        OrderOperatingRecord orderOperatingRecord = iOrderOperatingRecordService.selectOrderOperatingRecordByOrderIdAndOperatingType(orderId,OrderOperatingRecord.OPERATING_TYPE_MANUAL);
        if (ObjectUtil.isEmpty(orderOperatingRecord)) {
            orderOperatingRecord = iOrderOperatingRecordService.selectOrderOperatingRecordByOrderId(orderId);
        }
        HouseInspection houseInspection = iHouseInspectionService.selectHouseInspectionByOrderIdAndInspectionStatus(orderId,HouseInspection.ORDER_STATE_USER_HOUSE_INSPECTION);
        if (ObjectUtil.isNotEmpty(houseInspection) && StrUtil.isNotBlank(houseInspection.getHouseImg())) {
            String[] split = houseInspection.getHouseImg().split(",");
            houseInspection.setHouseImgList(Arrays.asList(split));
        }

        if ( ObjectUtil.isNotEmpty(orderOperatingRecord) && !"System".equals(orderOperatingRecord.getOperatingBy())) {
            AppUser appUser = iAppUserService.selectAppUserByUserId(orderOperatingRecord.getOperatingBy());
            if (ObjectUtil.isEmpty(appUser)) {
                User user = iUserService.selectUserByUserId(orderOperatingRecord.getOperatingBy());
                if (ObjectUtil.isNotEmpty(user)) {
                    orderOperatingRecord.setOperatingName(user.getName());
                    orderOperatingRecord.setOperatingPhone(user.getMobile());
                    orderOperatingRecord.setOperatingDepartment(user.getDepartmentName());
                }
            } else {
                orderOperatingRecord.setOperatingName(appUser.getName());
                orderOperatingRecord.setOperatingPhone(appUser.getPhone());
            }
        }

        orderInfoVO.setOrder(order);
        orderInfoVO.setCommunityHouseUserList(communityHouseUserList);
//        orderInfoVO.setHouseInspectionFacilitieList(houseInspectionFacilityList);
        orderInfoVO.setOrderOperatingRecord(orderOperatingRecord);
        orderInfoVO.setHouseInspection(houseInspection);
        return orderInfoVO;
    }

    @Override
    public List<Order> selectOrderListByHouse(Order order) {
        return orderMapper.selectOrderList(order);
    }

    @Override
    public List<HouseConditionVO> selectOrderCondition(Order order) {
        List<HouseConditionVO> houseConditionVOS = orderMapper.selectOrderCondition(order);
        List<HouseConditionVO> houseConditionVOouts = orderMapper.selectOrderConditionOut(order);
        houseConditionVOS.addAll(houseConditionVOouts);
        return houseConditionVOS;
    }

    private BigDecimal computedBalance(Order order, OrderAmountItem cashPledgeItem, OrderAmountItem houseItem, List<OrderAmountItem> reletItems, BigDecimal bigDecimal, HousingAllocation housingAllocation) {
        if (ObjectUtil.isNotEmpty(cashPledgeItem)) {
            bigDecimal = BigDecimal.valueOf(cashPledgeItem.getItemSum());
        }
        if (ObjectUtil.isNotEmpty(houseItem)) {
            bigDecimal = this.houseComputationalFormula(houseItem, order, bigDecimal, housingAllocation);
        }
        if (CollectionUtil.isNotEmpty(reletItems)) {
            for (OrderAmountItem reletItem : reletItems) {
                bigDecimal = this.houseComputationalFormula(reletItem, order, bigDecimal, housingAllocation);
            }
        }
        return bigDecimal;
    }

    public BigDecimal houseComputationalFormula(OrderAmountItem orderAmountItem, Order order, BigDecimal decimal, HousingAllocation housingAllocation) {
        if (StrUtil.isEmpty(orderAmountItem.getAmountDetail())) {
            if (HousingAllocation.APARTMENT_TYPE_SHORT.equals(housingAllocation.getApartmentType())) {
                if (order.getRealityCheckOutTime().before(orderAmountItem.getOrderCheckOutTime())) {
                    //订单真是退房时间在收费项预定时间之前，则true
                    if (order.getRealityCheckOutTime().before(orderAmountItem.getOrderCheckInTime())) {
                        //短租余额计算
                        long day = DateUtil.between(orderAmountItem.getOrderCheckInTime(), orderAmountItem.getOrderCheckOutTime(), DateUnit.DAY);
                        decimal = BigDecimal.valueOf(orderAmountItem.getGoodsAmount()).multiply(BigDecimal.valueOf(day)).add(decimal);
                    }else {
                        //短租余额计算
                        long day = DateUtil.between(order.getRealityCheckOutTime(), orderAmountItem.getOrderCheckOutTime(), DateUnit.DAY);
                        decimal = BigDecimal.valueOf(orderAmountItem.getGoodsAmount()).multiply(BigDecimal.valueOf(day)).add(decimal);
                    }
                }
            }
        } else {
            List<AmountDetailDTO> detailDTOList = GsonUtil.toBeanCollectors(orderAmountItem.getAmountDetail(), new TypeToken<List<AmountDetailDTO>>() {
            }.getType());
            if (HousingAllocation.APARTMENT_TYPE_SHORT.equals(housingAllocation.getApartmentType())) {
                long checkOutTime = order.getRealityCheckOutTime().getTime();
                for (AmountDetailDTO amountDetailDTO : detailDTOList) {
                    //订单的实际退房时间在订单金额详细过程中的时间段比较--时间段集合的结束时间在订单实际退房时间之后
                    if (amountDetailDTO.getEndTime().getTime() - checkOutTime > 0) {
                        long day;
                        if (amountDetailDTO.getStartTime().getTime() - checkOutTime >= 0) {//时间段集合的开始时间在订单实际退房时间之后
                            day = DateUtil.between(amountDetailDTO.getStartTime(), amountDetailDTO.getEndTime(), DateUnit.DAY);
                        } else {
                            day = DateUtil.between(order.getRealityCheckOutTime(), amountDetailDTO.getEndTime(), DateUnit.DAY);
                        }
                        decimal = BigDecimal.valueOf(amountDetailDTO.getGoodsAmount()).multiply(BigDecimal.valueOf(day)).add(decimal);
                    }
                }
            } else if (HousingAllocation.APARTMENT_TYPE_LONG.equals(housingAllocation.getApartmentType())) {
                //比较入住时间和用户退房时间的月份
                long month = DateUtil.betweenMonth(order.getCheckInTime(), order.getRealityCheckOutTime(), false);
                int compareTo = detailDTOList.get(0).getPurchaseQuantity().compareTo((double) month);
                BigDecimal dayDecimal = BigDecimal.ZERO;
                if (compareTo > 0) {
                    //>0则说明该笔订单是提前退房的，算余额
                    //还需要具体到天数
                    //计算提前退房时间到退房时间的月份
                    //long months = ChronoUnit.MONTHS.between(this.parseDate(order.getRealityCheckOutTime()), this.parseDate(orderAmountItem.getOrderCheckOutTime()));
                    //LocalDate localDate = order.getRealityCheckOutTime().toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
                    //LocalDate plusMonths = localDate.plusMonths(months);
                    //long realityCheckOutTimestamp = plusMonths.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
                    //if (realityCheckOutTimestamp != orderAmountItem.getOrderCheckOutTime().getTime() && orderAmountItem.getOrderCheckOutTime().getTime() - realityCheckOutTimestamp > 0) {
                    //    LocalDateTime localDateTime = LocalDateTime.ofInstant(orderAmountItem.getOrderCheckOutTime().toInstant(), ZoneId.systemDefault());
                    //    long days = ChronoUnit.DAYS.between(plusMonths, localDateTime);
                    //    int yearOfMonth = plusMonths.getMonthValue();
                    //    long dayOfYear = 0;
                    //    if (yearOfMonth == 1 || yearOfMonth == 3 || yearOfMonth == 5 || yearOfMonth == 7 || yearOfMonth == 8 || yearOfMonth == 10 || yearOfMonth == 12) {
                    //        dayOfYear = 31;
                    //    } else if (yearOfMonth == 4 || yearOfMonth == 6 || yearOfMonth == 9 || yearOfMonth == 11) {
                    //        dayOfYear = 30;
                    //    } else if (yearOfMonth == 2) {
                    //        //根据年来判断2月份的天数
                    //        int plusYear = plusMonths.getYear();
                    //        if ((plusYear % 100 != 0 && plusYear % 4 == 0) || plusYear % 400 == 0) {
                    //            dayOfYear = 29;
                    //        } else {
                    //            dayOfYear = 28;
                    //        }
                    //    }
                    //    //商品单价除以月份总数乘以剩余天数
                    //    dayDecimal = BigDecimal.valueOf(detailDTOList.get(0).getGoodsAmount()).divide(BigDecimal.valueOf(dayOfYear), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(days));
                    //}
                    ////提前退租的月份乘以月份单价，再加上
                    //decimal = BigDecimal.valueOf(months).multiply(BigDecimal.valueOf(detailDTOList.get(0).getGoodsAmount())).add(dayDecimal);
                    //（押付数量减去已居住月份）乘以房租的月租金额
                    decimal = decimal.add(BigDecimal.valueOf(detailDTOList.get(0).getPurchaseQuantity()).subtract(BigDecimal.valueOf(month)).multiply(BigDecimal.valueOf(detailDTOList.get(0).getGoodsAmount())));
                }/* else {
                    //否则则说明余额已经不够，不需要退了
                    //还有押金的金额，不能覆盖掉
                    //decimal = BigDecimal.ZERO;
                }*/
            }
        }
        return decimal;
    }

    public void packagingData(Order order) {
        // 拼装数据
        // title = 小区名 + 户型 eg: 胜利小区三室两厅
        String communityName = order.getResidenceCommunityName();
        String houseType = "";
        Long room = order.getRoom();
        Long hall = order.getHall();
        Long toilet = order.getToilet();
        // 数字转中文
        String roomStr = cn.hutool.core.convert.Convert.numberToChinese(room, false);
        String hallStr = cn.hutool.core.convert.Convert.numberToChinese(hall, false);
        String toiletStr = cn.hutool.core.convert.Convert.numberToChinese(toilet, false);
        if (StrUtil.isNotBlank(roomStr) && room != 0) {
            houseType += roomStr + "室";
        }
        if (StrUtil.isNotBlank(hallStr) && hall != 0) {
            houseType += hallStr + "厅";
        }
        if (StrUtil.isNotBlank(toiletStr) && toilet != 0) {
            houseType += toiletStr + "卫";
        }
        // 房型
        order.setHouseType(houseType);
        // 标题
        String title = communityName + houseType;
        order.setTitle(title);
        // 面积
        BigDecimal area = order.getArea();
        order.setArea(area);

        // 当前楼层
        Long floor = order.getFloor();
        order.setFloor(floor);

        // 总楼层
        Long buildId = order.getBuildingNum();
        String buildingNumName = order.getBuildingNumName();
        Long totalFloor = iCommunityBuildService.getTotalFloor(buildId);
        order.setTotalFloor(totalFloor);


        String fullAddress = order.getProvinceName() + order.getCityName() + order.getDistrictName() + order.getAddress()
                + order.getStreetName() + order.getResidenceCommunityName() + buildingNumName + "栋" + order.getUnitName() + "单元" + order.getRoomNumber() + "号";
        order.setFullAddress(fullAddress);

    }

    /**
     * 处理  订单房屋照片、订单金额小数点、失效时间
     */
    public void packagingOrder(Order order) {
        HousingAllocation housingAllocation = GsonUtil.toBean(order.getAllocationContent(), HousingAllocation.class);
        order.setApartmentPicture(housingAllocation.getApartmentPicture().split(",")[0]);
        order.setCleaningPrice(housingAllocation.getCleaningPrice());

        //将失效时间给前端
        Date createTime = order.getCreateTime();
        DateTime dateTime = DateUtil.offsetMinute(createTime, 30);

        order.setExpiresTimeTimestamp(dateTime.getTime());

        BigDecimal bigDecimal = DataUtils.IntegerToBigdecimal(DataUtils.doubleToInteger(order.getPriceAmounts()));
        order.setPriceAmountsDecimal(bigDecimal.toString());
        Integer apartmentType = housingAllocation.getApartmentType();
        order.setApartmentType(apartmentType);

    }

    public LocalDate parseDate(Date date) {
        // 将Date对象转换为ZonedDateTime对象
        LocalDateTime localDateTime = LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
        ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.systemDefault());
        return zonedDateTime.toLocalDate();
    }

}
